home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Apple Developer Connection Student Program
/
ADC Tools Sampler CD Disk 3 1999.iso
/
Cool Demos, SDKs, & Tools
/
Demos⁄Tools⁄Offers
/
Alpha ƒ
/
Tcl
/
Packages
/
elecExpansions.tcl
< prev
next >
Wrap
Text File
|
1999-04-07
|
24KB
|
840 lines
# Note from Vince: bug reports for this file should go to Tom Fetherston (install)
# (and probably to me too, since I have made some changes)
##===========================================================================
# elecExpansions, formerly acronymExpansions, formerly 'Word-Combinations Completion'
# AUTHOR
#
# Thomas R. Fetherston
# Internet: ranch1@earthlink.net
# USnail: 94 Lipp Ave, Pittsburgh, PA 15229-2001
#
################################################################################
# HISTORY
#
# modified who rev reason
# ---------------------- --- --- ------
# 12/2/97 trf 1.8 made changes to fix TeX expansions
# 05/22/1997 trf 1.7 Removed uses of 'oneSpace', this messed up stop
# locations.
# 05/14/1997 VD 1.6 Changed proc names to reflect new naming scheme
# 12/30/1996 trf 1.5 Modified so that this could be integrated with
# Vince Darley's Completion package.
# 07/14/1996 {01:21:20 PM} trf 1.4 Changed previous hit to a list of previous hits
# so hits would be offered only once.
# 07/14/1996 {12:04:37 PM} trf 1.3 work around for regexp to include a close bracket
# 07/12/1996 {12:53:38 AM} trf 1.2 Allow hint to be suffixed with certain puncuation
# marks and still be expanded. Added hint check.
# 07/11/1996 {10:29:51 PM} trf 1.1 Allow expansion of hint prefixed with non-
# alphabetic character(s).
# Ensure Hit used is followed with only one
# space.
# 07/05/1996 {10:28:20 PM} trf 1.0 Original
################################################################################
#
# Based on wordCompetion.tcl
# Originally composed by Mark Nagata (nagata@kurims.kyoto-u.ac.jp)
# for Alpha 5.76, 4/22/94.
#
# Modified by Tim van der Leeuw (tnleeuw@cs.vu.nl), 9/14/94.
# Modified by Tom Fetherston
# All the global variables needed to store state information between calls
# (start with __Gcw_)
#
# This provides a different kind of word completion than the
# distribution provides, (wordCompletion.tcl). When you find yourself
# typing a lot of variable and function names over and over, and these
# names are word-combinations where the name is formed by either
# capitalizing each word or separating them with an underscore, just
# type the initial letter of each word and invoke acronymExpansion
# instead.
#
#
# The idea of this modification is to allow you to type a string
# consisting of the initial letters of the words that have been joined
# to make up a variable, function, or procedure name. This is often
# shorter and more natural than typing a few letter and using
# wordCompletion. As I developed this routine I found that a regexp
# for more than three letters caused search to choke so only those
# letters of a "hint" are significant. A three letter pattern is used
# for the search. After a possible hit is located, it is turned into
# an acronym and checked against the "hint"
##
# to do list
# ----------
# change __Gcw_prevHint to a list of previous hits so we only get new hits
# monitor the character(s) to the right of the cursor point so the
# automatic, "oneSapce" can be suppressed, e.g. if a comma, close
# "bracket", or return immediately follows, let the inserted text abut
# it without a space
#
# cause a hint that ends in an '[' to search for a hit that is a
# parameterized routine. (what is a routine depends on the mode), and
# invoke a "replacement that includes a template for the proper number
# of arguments
# let numerals play a role in finding a "Hit"
# let an invocation include a file or list to be searched instead of
# the current window.
##
# The string you are going to use expansion on is entered in
# lowercase. The words in the target you are trying to hit have to
# start with a capital (except the first word), or, be separated by an
# underscore.
# The hint can be embedded between non-alphabetic characters and
# certain punctuation marks ( '[', '(', '{', ',', ';', ':', ''', '"',
# ']', ')', '}' ). The expanded hint remains so embedded, and the
# cursor appears one space beyond the trailing punctuation.
# e.g. if sin($gl) was expanded, we would get sin($__Gcw_len) (in this
# file). similarly, mouse($gph, would expand to
# 'mouse($__Gcw_prevHint, ', done twice, we would get
# 'mouse($__Gcw_prevHit, '.
#
##
# The following binding is just a suggestion. It is the one that I
# like best, I have this in my pref.tcl file
#
# ascii 0x20 <c> bind::Expansion
# i.e. command-<space>
#================================================================================
alpha::extension elecExpansions 9.0b3 {
alpha::package require elecBindings 9.0b1
lunion flagPrefs(Electrics) listPickIfMultExpds
# similarly for expansions
newPref flag listPickIfMultExpds 0
} maintainer {
"Tom Fetherston" "" ""
} uninstall this-file help {file "ElecCompletions Help"}
set __Gcw_prevHintPos -1
set __Gcw_prevHint {}
set __Gcw_prevsrcListName {}
ensureset __Gcw_already_expanding error
ensureset __Gcw_pos_expanding -1
##
# -------------------------------------------------------------------------
#
# "bind::Expansion" --
#
# If we're already completing, jump to that procedure, else go
# through a mode-dependent list of expansion procedures given by the
# array 'completions', these return either '1' to indicate
# termination, or '0' to say either that they failed or that they
# succeeded and that further expansion procedures may be applied.
# -------------------------------------------------------------------------
##
proc bind::Expansion {} {
if {![completion::tabDeleteSelection]} return
global __Gcw_already_expanding
if {[elec::notAlreadyExpanding]} {
set __Gcw_already_expanding error
if {[expansion::user]} return
set m [modeALike]
global expanders
set curPos [getPos]
if {![catch {set expandersList $expanders($m)}]} {
foreach e $expandersList {
if {[completion $m $e]} return
}
}
#if none of the expanders succeeded, (or, don't exist) try
if {[pos::compare [getPos] == $curPos]} {
expansion::acronym
}
}
}
##
# -------------------------------------------------------------------------
#
# "elec::notAlreadyExpanding" --
#
# Call this to check if we should divert directly to a previously
# registered expansion procedure instead of starting from scratch.
# -------------------------------------------------------------------------
##
proc elec::notAlreadyExpanding {} {
global __Gcw_already_expanding __Gcw_pos_expanding
# do the old expansion if possible
if {[pos::compare $__Gcw_pos_expanding == [getPos]]} {
return [catch {elec::completion [modeALike] $__Gcw_already_expanding}]
} else {
return 1
}
}
##
# -------------------------------------------------------------------------
#
# "elec::alreadyExpanding" --
#
# If a expansion routine has been called once, and would like to be
# called again (to cycle through a number of possibilities), then it
# should register itself with this procedure.
# -------------------------------------------------------------------------
##
proc elec::alreadyExpanding { proc } {
global __Gcw_already_expanding __Gcw_pos_expanding
# store the given expansion
set __Gcw_already_expanding $proc
set __Gcw_pos_expanding [getPos]
}
##
# These declare, in order, the names of the expander procedures for
# each mode. The actual procedure must be named
# '${mode}Expansion::${listItem}', unless the item is 'expansions::*'
# in which case that actual procedure is called.
##
#===========================================================================
set expanders(TeX) {ExCmd}
set expanders(Tcl) {}
set expanders(C) {}
# just so we have one!
set userExpansionw(date) {◊kill0◊[lindex [mtime [now]] 0]}
namespace eval expansion {}
proc expansion::user { {cmd ""} } {
if {![string length $cmd]} { set cmd [completion::lastWord] }
if {[containsSpace $cmd]} { return 0 }
set curPos [getPos]
elec::findCmd $cmd userExpansionw
#if the above call resulted in a detectable action, (i.e. the
# current positon has change), return 1
if {[pos::compare [getPos] == $curPos]} {
return 0
} else {
return 1
}
}
# -------------------
proc expansion::acronym {} {
global __Gcw_len
global __Gcw_prevHintPos
global __Gcw_prevHint
global __Gcw_endPrevRpl
global __Gcw_prevHits
global __Gcw_patt
global __Gcw_nextStart
global __Gcw_above_BELOW
set To [getPos]
set lastChar [lookAt [pos::math $To - 1]]
set hintCapper [lookAt $To]
switch -- $hintCapper {
"\)" -
"\}" -
"\]" -
" " -
"\t" {
if {$lastChar != ","} {
set trailingWhite {}
} else {
set trailingWhite " "
}
}
"default" {
switch -- $lastChar {
"\(" -
"\{" -
"\{" -
" " -
"\t" {
set trailingWhite {}
}
default {
set trailingWhite " "
}
}
}
}
backwardWord
set From [getPos]
# adjust From to prune any non alphabetic prefix
set hint [getText $From $To]
# The following variables may not come into existence in the regexp
# below, so set up defaults.
set tail ""
set punc ""
#can not seem to include a close brack as below
#regexp {([a-zA-Z_]+)([\(\{\[,;:'"\}\)\]])*[ ]*$} $hint tail hint punc
#work around on above
regexp {([a-zA-Z0-9_]+)(([\(\{\[,;:'"\}\)])*(\])*([\(\{\[,;:'"\}\)])*)[ ]*$} $hint tail hint punc
set From [pos::math $To - [string length $tail]]
# this is a 1stTry, but hint is illegal
if {[pos::compare $From != $__Gcw_prevHintPos]} {
if {[regexp {[0-9_]} $hint] > 0} {
alertnote "Ilegal hint, must have only letters in it."
select $From $To
set __Gcw_prevHintPos -1
return
} elseif {$From==$To} {
alertnote "Was not able to find any hint."
set __Gcw_prevHintPos -1
return
}
}
# adjust To, leaving trailing spaces or tabs
set To [expr $From + [string length [append junk $hint $punc]]]
# if (Trying to complete a new hint)
if {[pos::compare $From != $__Gcw_prevHintPos]} {
set __Gcw_prevHint $hint
set __Gcw_prevHits {}
set __Gcw_len [string length $hint]
set __Gcw_patt [pFI $hint]
set __Gcw_above_BELOW 0
set start [pos::math $From - 1]
set beg {}; set end {}
set foundAbove 0
elec::_searchAboveForHit start beg end Hit foundAbove
if {$foundAbove} {
lappend __Gcw_prevHits $Hit
# put in the Hit,
set replacement {}
append replacement $Hit $punc $trailingWhite
replaceText $From $To $replacement
goto [pos::math $From + [string length $replacement]]
# oneSpace
message "found above."
set __Gcw_prevHintPos $From
set __Gcw_endPrevRpl [getPos]
elec::_adjustGlobals __Gcw_endPrevRpl __Gcw_above_BELOW __Gcw_nextStart
return
}
set start $To
set beg {}; set end {}
set __Gcw_above_BELOW 1
set foundBelow 0
elec::_searchBelowForHit start beg end Hit foundBelow
if {$foundBelow} {
lappend __Gcw_prevHits $Hit
# put in the Hit,
set replacement {}
append replacement $Hit $punc $trailingWhite
replaceText $From $To $replacement
goto [pos::math $From + [string length $replacement]]
message "found below."
set __Gcw_prevHintPos $From
set __Gcw_endPrevRpl [getPos]
elec::_adjustGlobals __Gcw_endPrevRpl __Gcw_above_BELOW __Gcw_nextStart
return
}
#No Hit for this hint exists
# goto $To
# backwardWordSelect
select $From $To
set __Gcw_prevHintPos -1
return
# else: we are re-trying the previous hint
} else {
while 1 {
#pre-set fndMsg, in case there is a valid Hit for this iteration
if {$__Gcw_above_BELOW} {
set fndMsg "found below."
} else {
set fndMsg "found above."
}
set start $__Gcw_nextStart
set beg {}; set end {}
set foundByContinuedSearch 0
elec::_continueSearchForHit start beg end Hit foundByContinuedSearch
if {$foundByContinuedSearch} {
#if (this Hit is not the same as the last one)
if {[lsearch -exact $__Gcw_prevHits $Hit] == -1} {
#add the hit to the list of previous hits
lappend $__Gcw_prevHits $Hit
# put in the Hit,
if {($punc == ",") && [info exists tail]} {
set trailingWhite " "
}
# put in the Hit,
set replacement {}
append replacement $Hit $punc $trailingWhite
replaceText $From $To $replacement
goto [pos::math $From + [string length $replacement]]
message $fndMsg
set __Gcw_endPrevRpl [getPos]
elec::_adjustGlobals __Gcw_endPrevRpl __Gcw_above_BELOW __Gcw_nextStart
return
#else: this Hit does not differ from the last
} else {
elec::_adjustGlobals __Gcw_endPrevRpl __Gcw_above_BELOW __Gcw_nextStart
}
#else: another Hit was not found
} else {
#if (no more Hits can exist, because we have searched all the text)
if {$__Gcw_above_BELOW} {
message "Not found."
# goto $To
# backwardWordSelect
select $__Gcw_prevHintPos $__Gcw_endPrevRpl
set __Gcw_prevHintPos -1
return
#else: we haven't tried BELOW
} else {
set __Gcw_above_BELOW 1
set __Gcw_nextStart $__Gcw_endPrevRpl
}
}
}
}
}
# ---
proc pFI wordStarters {
proc firstPost {char} {
return [format "(%s|%s)" [string toupper $char] $char ]
}
proc fencePost {char} {
return [format {(%1$s|_%1$s|_%2$s)} [string toupper $char] $char ]
}
set identifierLeader {(_|__)?}
set wordTail {[a-z0-9]*}
set identifierTail {[a-zA-Z0-9_]*}
set idx_Last [expr {[string length $wordStarters]-1}]
if {$idx_Last>2} {set idx_Last 2}
set searchPatt $identifierLeader
append searchPatt [firstPost [string index $wordStarters 0]]
append searchPatt $wordTail
for {set i 1} {$i < $idx_Last} {incr i} {
append searchPatt [fencePost [string index $wordStarters $i]]
append searchPatt $wordTail
}
append searchPatt [fencePost [string index $wordStarters $i]]
append searchPatt $identifierTail
return $searchPatt
}
#Note: in all the following scripts that start with uplevel…, the
# agrguments are "fake", and serve only to show what variables
# are used by these macro-like subroutines. Their primary purpose
# is to make the above code more readable. Each is started with
# an underscore to indicate that they are internal to another
# routine, and should not be called by themselves.
# ------------------ -in-- -out--------(bool)-
proc elec::_searchAboveForHit {start beg end Hit success} {
uplevel {
set BegEnd {-1 -1}
set moreToSearch 1
while {$moreToSearch} {
set foundAbove [expr {![catch {search -s -f 0 -r 1 -i 0 -m 1 -- $__Gcw_patt $start} BegEnd]}]
if {!$foundAbove} {unset BegEnd ; break}
set beg [lindex $BegEnd 0]
set end [lindex $BegEnd 1]
unset BegEnd
set Hit [getText $beg $end]
set fullMatch [elec::acronymsAreEqual $hint $Hit]
if {$fullMatch} {
break
} else {
set foundAbove 0
}
if {[pos::compare $beg <= [minPos]]} {
set moreToSearch 0
} else {
set start [pos::math $beg-1]
}
}
}
}
# ------------------ -in-- -out--------(bool)-
proc elec::_searchBelowForHit {start beg end Hit success} {
uplevel {
set BegEnd {-1 -1}
set moreToSearch 1
while {$moreToSearch} {
set foundBelow [expr {![catch {search -s -f 1 -r 1 -i 0 -m 1 -- \
$__Gcw_patt $start} BegEnd]}]
if {!$foundBelow} {unset BegEnd ; break}
set beg [lindex $BegEnd 0]
set end [lindex $BegEnd 1]
set Hit [getText $beg $end]
unset BegEnd
set fullMatch [elec::acronymsAreEqual $hint $Hit]
if {$fullMatch} {
break
} else {
set foundBelow 0
}
if {[pos::compare $end >= [maxPos]]} {
set moreToSearch 0
} else {
set start [expr $end]
}
}
}
}
# --------------------- -in-- -out--------(bool)-
proc elec::_continueSearchForHit {start beg end Hit success} {
uplevel {
set BegEnd {-1 -1}
set moreToSearch 1
while {$moreToSearch} {
set foundByContinuedSearch [expr {![catch {search -s -f $__Gcw_above_BELOW -r 1 -i 0 -m 1 -- \
$__Gcw_patt $__Gcw_nextStart} BegEnd]}]
if {!$foundByContinuedSearch} {unset BegEnd ; break}
set beg [lindex $BegEnd 0]
set end [lindex $BegEnd 1]
set Hit [getText $beg $end]
unset BegEnd
set fullMatch [elec::acronymsAreEqual $__Gcw_prevHint $Hit]
if {$fullMatch} {
break
} else {
set foundBelow 0
}
if {[pos::compare $end >= [maxPos]]} {
set moreToSearch 0
} else {
elec::_adjustGlobals __Gcw_endPrevRpl __Gcw_above_BELOW __Gcw_nextStart
}
}
}
}
# -------------- -in------------- -mod------------- -out-----------
proc elec::_adjustGlobals {__Gcw_endPrevRpl __Gcw_above_BELOW __Gcw_nextStart} {
uplevel {
if {$__Gcw_above_BELOW} {
set __Gcw_nextStart $end
} else {
set __Gcw_nextStart [pos::math $beg - 1]
if {[pos::compare $__Gcw_nextStart <= [minPos]]} {
set __Gcw_above_BELOW 1
set __Gcw_nextStart $__Gcw_endPrevRpl
}
}
}
}
# ----------(bool) -in- ---------------
proc elec::acronymsAreEqual {hint wordCombination} {
set splitOnUndrS [split $wordCombination {_}]
set shoe {}
foreach part $splitOnUndrS {
if {$part == {}} continue
set part [split $part {}]
set part [lreplace $part 0 0 [string toupper [lindex $part 0]]]
set part [join $part {}]
append shoe $part
}
regsub -all \[a-z0-9\] $shoe {} shoe
return [expr {![string compare [string toupper $hint] $shoe]}]
}
##
# -------------------------------------------------------------------------
#
# "elec::acronymListExpansions" --
#
# Given a an acronym of the sub-words in a 'multi-word command' (the
# 'hint') and the name of a list to search, that list consisting of
# acronyms-command pairs on separate lines that have been placed in
# alphabetical order and starting/ending with a return, this proc
# returns a list of all pairs that have the hint as their first
# element or'0' if there were none.
#
# Based on Vince Darley's modeListCompletions
# -------------------------------------------------------------------------
##
proc elec::acronymListExpansions { hint dictName } {
global $dictName
set reg {(\n}
append reg $hint { +[^\n]+)+}
if {[regexp $reg [set $dictName] pairs]} {
set odd 1
foreach m $pairs {
if {$odd % 2 != 0} {
incr odd
continue
}
incr odd
append matches $m " "
}
return $matches
} else {
return 0
}
}
proc elec::expandThis { cmd matches {isdbllist 0} {forcequery 0}} {
global possMatches returnedMatch listPickIfMultExpds
set possMatches $matches
set mquery [set match [lindex $matches 0]]
if {$isdbllist} { set match [lindex [lindex $match 0] 0]}
if { [set cmdnum [llength $matches]] == 1 || $match == $cmd } {
# It's unique or already a command, so insert it
backwardDeleteWord
elec::commandPrefix
insertText $match
return $match
} else {
set item [lindex $matches [incr cmdnum -1]]
if {$isdbllist} { set item [lindex [lindex $item 0] 0] }
set num 1
set correspondingNum 1
set numberedChoices "\{"
set currChoiceSet ""
set setIdx 0
set multiSets 0
set pickNumOfStartIn(0) $correspondingNum
foreach m $matches {
append numberedList "\{$num $m\} "
#make up a list of choiceSets, where eadh choice set has < 79
# characters
if {[string length "$currChoiceSet$correspondingNum $m "] < 77} {
append numberedChoices "$correspondingNum $m "
append currChoiceSet "$correspondingNum $m "
set setAndNum($num) [list $setIdx $correspondingNum]
} else {
incr setIdx
set correspondingNum 1
append numberedChoices "m…\} \{$correspondingNum $m "
set currChoiceSet "$correspondingNum $m "
set setAndNum($num) [list $setIdx $correspondingNum]
set pickNumOfStartIn($setIdx) $num
set multiSets 1
}
incr correspondingNum
incr num
}
if {$multiSets} {
append numberedChoices "b…\}"
} else {
append numberedChoices "\}"
}
if { $listPickIfMultExpds } {
beep
if {[catch { set choice [listpick -p "Pick an expansion" $numberedList]}]} {
message "Cancelled"
return 1
} else {
backwardDeleteWord
elec::commandPrefix
set choice [lindex $choice 1]
insertText $choice
return $choice
}
} else {
set pickNum 1
set promptNum $pickNum
set currChoiceSet_idx 0
set c "\t"
backwardDeleteWord
elec::commandPrefix
insertText [lindex $matches 0]
while {[set c] == "\t"} {
set currChoiceSet_idx [lindex $setAndNum($pickNum) 0]
set currChoiceSet [lindex $numberedChoices $currChoiceSet_idx]
#look up what number in the currChoiceSet corresponds to the pickNum
set currNum [lindex $setAndNum($pickNum) 1]
regsub "$currNum " $currChoiceSet "=>" choices
global returnedMatch
set returnedMatch ""
message $choices
set c [getChar]
set c [string tolower $c ]
scan $c "%c" decRep
if {$decRep == 27} {
set c "esc"
}
switch -- $c {
"\t" {
incr pickNum
if {$pickNum > [llength $matches]} {
set pickNum 1
}
backwardDeleteWord
elec::commandPrefix
insertText [lindex $matches [expr {$pickNum -1}]]
#set things up so we cylce to the next choice
continue
}
" " -
"\\" -
"\r" {
#these keys indicate that we are satisfied with the current choice,
# just insert the key pressed
# alertnote "you pressed a return, \\, or space"
# alertnote "pickNum = $pickNum"
return [list [lindex $matches [expr {$pickNum -1}]] $c]
}
"m" {
#when there are more choices than can be diplayed on the statusline
# pressing 'm', will get the next set of choices
if {[string match "*m…" $currChoiceSet]} {
set pickNum $pickNumOfStartIn([expr {$currChoiceSet_idx +1}])
}
if {[string match "*b…" $currChoiceSet]} {
set pickNum 1
}
backwardDeleteWord
elec::commandPrefix
insertText [lindex $matches [expr {$pickNum -1}]]
#set things up so we cylce to the next choice
set c "\t"
continue
}
"b" {
#when there are more choices than can be diplayed on the statusline
# pressing 'b', will get the first set of choices
set pickNum 1
set c "\t"
backwardDeleteWord
elec::commandPrefix
insertText [lindex $matches [expr {$pickNum -1}]]
#set things up so we cylce to the next choice
continue
}
"esc" {
#when you want to bypass this and get to acronymExpansion
backwardDeleteWord
insertText $cmd
return 0
}
"default" {
#see if c is, or can be converted to, a number in the range 1-9
set strPos [string first $c "asdfghjkl123456789"]
if {$strPos == -1} {
beep
return
}
set numberChoosen [expr {$strPos % 9}]
if {$numberChoosen > [llength $possMatches]} {
beep
return
}
#alertnote "you choose number $numberChoosen"
set returnedMatch [lindex $possMatches [expr {$pickNumOfStartIn($currChoiceSet_idx) + $numberChoosen -1}]]
}
}
# catch {statusPrompt -f $choices statusLineChooser}
if {$returnedMatch != ""} {
backwardDeleteWord
elec::commandPrefix
insertText $returnedMatch
return $returnedMatch
}
}
}
return ""
}
}
proc elec::commandPrefix {} {
global mode
switch -- $mode {
"TeX" {
set pos [getPos]
set bol [getText [lineStart $pos] $pos]
switch -glob $bol {
"*\\begin\{" -
"*\\end\{" -
"*\\" {
return
}
"default" {
insertText "\\"
}
}
}
}
}